/*
 * @(#)XFDMTokenMarker.java        0.1 22-Mar-2009
 *
 * Copyright 2008 Thomas Ford. All rights reserved.
 */


package edu.napier.soc.xfdm.uci;

import javax.swing.text.Segment;
import org.syntax.jedit.KeywordMap;
import org.syntax.jedit.tokenmarker.Token;
import org.syntax.jedit.tokenmarker.TokenMarker;

/**
 * 
 * @version 0.1, 22-Mar-2009
 * @author  Thomas Ford
 */
public class XFDMTokenMarker extends TokenMarker {

	public XFDMTokenMarker() {

		this.keywords = getKeywords();
	}
	public static KeywordMap getKeywords()
	{
		if(cKeywords == null)
		{
			cKeywords = new KeywordMap(false);

			// data types
			cKeywords.add("char",Token.KEYWORD3);
			cKeywords.add("real",Token.KEYWORD3);
			cKeywords.add("entity",Token.KEYWORD3);
			cKeywords.add("integer",Token.KEYWORD3);
			cKeywords.add("boolean",Token.KEYWORD3);
			cKeywords.add("string",Token.KEYWORD3);

			// constraints
			cKeywords.add("fixed",Token.KEYWORD3);
			cKeywords.add("unique",Token.KEYWORD3);
			cKeywords.add("total",Token.KEYWORD3);
			
			// agg functions
			cKeywords.add("count", Token.KEYWORD2);
			cKeywords.add("min", Token.KEYWORD2);
			cKeywords.add("max", Token.KEYWORD2);
			cKeywords.add("sum", Token.KEYWORD2);
			cKeywords.add("average", Token.KEYWORD2);

			// literals
			cKeywords.add("true",Token.LITERAL2);
			cKeywords.add("false",Token.LITERAL2);
			cKeywords.add("null",Token.LITERAL2);

			// quantifiers
			cKeywords.add("have", Token.KEYWORD1);
			cKeywords.add("at", Token.KEYWORD1);
			cKeywords.add("least", Token.KEYWORD1);
			cKeywords.add("most", Token.KEYWORD1);
			cKeywords.add("exactly", Token.KEYWORD1);
			cKeywords.add("in",Token.KEYWORD1);
			cKeywords.add("on",Token.KEYWORD1);
			cKeywords.add("let",Token.KEYWORD1);
			cKeywords.add("all", Token.KEYWORD1);
			cKeywords.add("no", Token.KEYWORD1);
			cKeywords.add("has",Token.KEYWORD1);
			cKeywords.add("some",Token.KEYWORD1);

			// boolean operators
			cKeywords.add("and",Token.KEYWORD1);
			cKeywords.add("or",Token.KEYWORD1);
			cKeywords.add("not",Token.KEYWORD1);


			cKeywords.add("for",Token.KEYWORD1);
			cKeywords.add("the",Token.KEYWORD1);
			cKeywords.add("load",Token.KEYWORD1);
			cKeywords.add("each",Token.KEYWORD1);
			cKeywords.add("into",Token.KEYWORD1);
			cKeywords.add("a",Token.KEYWORD1);
			cKeywords.add("new",Token.KEYWORD1);
			cKeywords.add("delete",Token.KEYWORD1);
			cKeywords.add("include",Token.KEYWORD1);
			cKeywords.add("drop", Token.KEYWORD1);
			cKeywords.add("exclude",Token.KEYWORD1);
			cKeywords.add("the",Token.KEYWORD1);
			cKeywords.add("declare",Token.KEYWORD1);
			cKeywords.add("such",Token.KEYWORD1);
			cKeywords.add("that",Token.KEYWORD1);
			cKeywords.add("constraint",Token.KEYWORD1);
			cKeywords.add("print",Token.KEYWORD1);
		}
		return cKeywords;
	}

	// private members
	private static KeywordMap cKeywords;

	public byte markTokensImpl(byte token, Segment line, int lineIndex)
	{
		char[] array = line.array;
		int offset = line.offset;
		lastOffset = offset;
		lastKeyword = offset;
		int length = line.count + offset;

loop:		for(int i = offset; i < length; i++)
		{
			int i1 = (i+1);

			char c = array[i];

			switch(token)
			{
			case Token.NULL:
				switch(c)
				{
				case '"':
					doKeyword(line,i,c);
					addToken(i - lastOffset,token);
					token = Token.LITERAL1;
					lastOffset = lastKeyword = i;
					break;
				case '\'':
					doKeyword(line,i,c);
					addToken(i - lastOffset,token);
					token = Token.LITERAL2;
					lastOffset = lastKeyword = i;
					break;
				case '/':
					doKeyword(line,i,c);
					if(length - i > 1)
					{
						switch(array[i1])
						{
						case '/':
							addToken(i - lastOffset,token);
							addToken(length - i,Token.COMMENT1);
							lastOffset = lastKeyword = length;
							break loop;
						}
					}
					break;
				default:
					if(!Character.isLetterOrDigit(c)
						&& c != '_')
						doKeyword(line,i,c);
					break;
				}
				break;
			case Token.LITERAL1:
				if(c == '"')
				{
					addToken(i1 - lastOffset,token);
					token = Token.NULL;
					lastOffset = lastKeyword = i1;
				}
				break;
			case Token.LITERAL2:
				if(c == '\'')
				{
					addToken(i1 - lastOffset,Token.LITERAL1);
					token = Token.NULL;
					lastOffset = lastKeyword = i1;
				}
				break;
			default:
				throw new InternalError("Invalid state: "
					+ token);
			}
		}

		if(token == Token.NULL)
			doKeyword(line,length,'\0');

		switch(token)
		{
		case Token.LITERAL1:
		case Token.LITERAL2:
			addToken(length - lastOffset,Token.INVALID);
			token = Token.NULL;
			break;
		case Token.KEYWORD2:
			addToken(length - lastOffset,token);
			token = Token.NULL;
			break;
		default:
			addToken(length - lastOffset,token);
			break;
		}

		return token;
	}

	private KeywordMap keywords;
	private int lastOffset;
	private int lastKeyword;

	private boolean doKeyword(Segment line, int i, char c)
	{
		int i1 = i+1;

		int len = i - lastKeyword;
		byte id = keywords.lookup(line,lastKeyword,len);
		if(id != Token.NULL)
		{
			if(lastKeyword != lastOffset)
				addToken(lastKeyword - lastOffset,Token.NULL);
			addToken(len,id);
			lastOffset = i;
		}
		lastKeyword = i1;
		return false;
	}
}
